package org.bpm.view
{
	import flash.display.MovieClip;
	import flash.display.Shape;
	import flash.display.Sprite;
	import flash.events.Event;
	import flash.events.MouseEvent;
	import flash.geom.Point;
	import flash.geom.Rectangle;
	
	import org.bpm.events.ProcessMapEvent;
	import org.bpm.interfaces.IMapElement;
	import org.bpm.models.MapLineModel;
	import org.bpm.utils.AnimatedDisplayUtil;
	import org.bpm.utils.UIGenerator;
	/**
	 * This class calculate the display rules of transition lines.
	 * Only straight lines without curve are available in this library.
	 * */
	public class MapLine extends Sprite implements IMapElement
	{
		public var model:MapLineModel;
		private var lineElement:IMapElement;
		
		public function MapLine(pModel:MapLineModel)
		{
			super();
			model = pModel;
			mouseChildren = false;
			buttonMode = true;
			
			addEventListener(Event.REMOVED_FROM_STAGE, onRemoveFromStage);
			addEventListener(MouseEvent.ROLL_OVER, overHandler);
			addEventListener(MouseEvent.ROLL_OUT, outHandler);
			addEventListener(MouseEvent.CLICK, clickHandler);
		}
		private function overHandler(pEvent:MouseEvent):void{
			highlight();
			dispatchEvent(new ProcessMapEvent(ProcessMapEvent.MAP_ELEMENT_OVER, model.definitionList,true));
		}
		private function outHandler(pEvent:MouseEvent):void{
			if(!model.isDone) blur();	
			dispatchEvent(new ProcessMapEvent(ProcessMapEvent.MAP_ELEMENT_OUT,model.definitionList,true));
		}
		private function clickHandler(pEvent:MouseEvent):void{
			dispatchEvent(new ProcessMapEvent(ProcessMapEvent.MAP_ELEMENT_CLICK, model.definitionList,true));
		}
		
		private function onRemoveFromStage(pEvent:Event):void{
			removeEventListener(Event.REMOVED_FROM_STAGE, onRemoveFromStage);
			removeEventListener(MouseEvent.ROLL_OVER, overHandler);
			removeEventListener(MouseEvent.ROLL_OUT, outHandler);
			removeEventListener(MouseEvent.CLICK, clickHandler);
			
			if(lineElement){
				removeChild(lineElement as MovieClip);
				lineElement = null;
			}
			model = null;
		}
		
		public function highlight():void{
			if(lineElement) lineElement.highlight();
		}
		public function blur():void{
			if(lineElement) lineElement.blur();
		}
		public function updateLabel(pValue:String):void{
			lineElement.updateLabel(pValue);
		}
		/**
		 * Create arrow line between two nodes.
		 * */
		public function createByNodes(pStartNode:MapNode, pEndNode:MapNode):void{
			var rect:Rectangle = generateVisibleRect(pStartNode,pEndNode);
			lineElement = (new UIGenerator()).getUI("arrow");
			//trace(lineElement as ArrowMovieClip);
			ArrowMovieClip(lineElement).visibleRect = rect;
			ArrowMovieClip(lineElement).labelPosition = model.labelPosition;
			lineElement.width = rect.bottomRight.subtract(rect.topLeft).length;
			updateLabel(model.label);
			addChild(lineElement as MovieClip);
			//AnimatedDisplayUtil.animatedShow(lineElement as MovieClip);
			
			model.isDone ? highlight() : blur();
		}
		
		/**
		 * The arrow line shouldn't be beneath any of the node views,
		 * so we need to check the visible area of the line in order to display the arrow correctly.
		 * */
		private function generateVisibleRect(pStartNode:MapNode, pEndNode:MapNode):Rectangle{

			var rect1:Rectangle = pStartNode.model.rect;
			var rect2:Rectangle = pEndNode.model.rect;
			
			/**
			 * Arrow line should point to the center of these two nodes.
			 *  */
			var centerPoint1:Point = new Point(rect1.x + rect1.width/2, rect1.y + rect1.height/2);
			var centerPoint2:Point = new Point(rect2.x + rect2.width/2, rect2.y + rect2.height/2);
			
			var angle:Number = Math.atan2(centerPoint2.y- centerPoint1.y,centerPoint2.x - centerPoint1.x);
			var distance:Number = centerPoint2.subtract(centerPoint1).length;
			
			var startPoint:Point;
			var endPoint:Point;
			var d:Number;
			var tx:Number;
			var ty:Number;
			
			/**
			 * Because node views are possibly not rectangles, e.g. circles,
			 * we need use hitTestPoint to check the visible edge.
			 * */
			for(d = 1;d<distance;d++){
				tx = pStartNode.x + d * Math.cos(angle);
				ty = pStartNode.y + d * Math.sin(angle);
				if(!pStartNode.hitTestPoint(tx,ty,true)){
					startPoint = new Point(tx,ty);
					break;
				}
			}
			for(d = 1;d < distance;d++){
				tx = pEndNode.x - d * Math.cos(angle);
				ty = pEndNode.y - d * Math.sin(angle);
				if(!pEndNode.hitTestPoint(tx,ty,true)){
					endPoint = new Point(tx,ty);
					break;
				}
			}
			return new Rectangle(startPoint.x, startPoint.y, endPoint.x - startPoint.x, endPoint.y - startPoint.y);
		}
	}
}